home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Almathera Ten Pack 3: CDPD 3
/
Almathera Ten on Ten - Disc 3: CDPD3.iso
/
ab20
/
ab20_archive
/
languages
/
assembly
/
powervisor_113.lzh
/
docs
/
Debug
< prev
next >
Wrap
Text File
|
1992-02-13
|
49KB
|
1,095 lines
*-----------------*
* Tutorial: Debug * Thu Feb 13 21:27:56 1992
*-----------------*
===================== Commands used in this tutorial =========================
break Control breakpoints
debug Control debug tasks
disp Display integer
drefresh Refresh debug display
dscroll Scroll in debug display
dstart Set start programcounter in debug display
duse Set the default debug task
dwin Open/close 'Debug' logical window
info Ask information about a structure or node
list List structures
loadfd Load fd-file
prefs Set preferences
source Load source files for sourcelevel debugger
symbol Control symbols
trace Control tracing
unasm Disassemble memory
with Temporarily set the default debug task
===================== Functions used in this tutorial ========================
botpc Get the programcounter at the bottom of the display
toppc Get the programcounter at the top of the display
============================== Introduction ==================================
I don't think that you will be surprised if I tell you that PowerVisor can
even debug programs :-) This file explains how you should do this. It also
explains how you can make life easier with a fully customized
²fullscreen debugger. PowerVisor is a very powerful debugger. For example,
you can ³debug multiple tasks at the same time.
Note that PowerVisor is not really a ³source level debugger, although
you can load the source (even for C programs). The source will also follow
the current programcounter. In future I plan more support for local and
global variables in C. A stack watcher would also be a nice thing.
Note that the PowerVisor debug system works much better in the AmigaDOS 2.0
version. This is because AmigaDOS 2.0 has some nice features making life a
lot easier for the programmer. I'm sorry for all AmigaDOS 1.3 users.
The examples below work on AmigaDOS 1.3 and 2.0.
============================ Loading a program ===============================
There are several ways to load a program. The method you chooses depends on
what you really need. The 'debug' command is provided by PowerVisor to
control the ²debug tasks (or debug nodes). All the ²debug nodes can be found
in the ²'dbug' list. With the 'debug' command you can load a program, you can
unload a program and you can do other things as well.
=========================== An example session ===============================
This section contains a complete example debugging session. Note that we
do not use the ²fullscreen debugger in this example. Use of the fullscreen
debugger is explained later. It is recommended that you type each command
as it appears here. Note that the output given here assumes that you have
all preferences set to default values (use 'mode shex', 'prefs dmode f' and
'prefs debug 5 1' if you are not sure that the default values are used).
-------------------------- Starting the session ------------------------------
'debug n' is the recommended way to load a program. 'debug n' waits for
the next program that is started and interrupts it before the first
instruction is executed. To do this, 'debug n' patches the Dos ¹LoadSeg
function. Example :
< debug n <enter>
PowerVisor waits for you to start the program you want to debug. You
can start this program from the ¹WorkBench (click on the icon) or you
can start it from the Cli or Shell. I have provided an example program
with a ²resident breakpoint. Resident breakpoints are explained later
in this file. You can find this program in the ²'Examples' subdirectory.
CLI< examples/buggyprogram <enter>
or
CLI< run examples/buggyprogram <enter>
(Note ! Only use 'run' when 'run' is ¹resident or a built-in shell command,
in other words: don't use 'run' when 'run' itself must be loaded from disk
with 'loadseg'. You probably don't want to debug 'run' :-) 'run' is always
resident in AmigaDOS 2.0)
(Note ! When PowerVisor is waiting for a program you must be careful not
to use any other program (that is already running) that might use LoadSeg
for some other purpose. Fonts, for example, are loaded using LoadSeg)
(Note ! You can ¹interrupt 'debug n' with ¹<esc>)
'debug n' is the best way to load a debug task because the program runs
in exactly the same environment as the environment you get when you simply
run the program.
Allright, we have now loaded the program in memory.
< list dbug <enter>
> Debug task : Node Task InitPC TD ID Mode SMode TMode
> ---------------------------------------------------------------------------
> Background CLI : 07EA7A58 07EF8FA8 07EAA7D8 FF FF NONE WAIT NORM
Most of this information is rather technical and is not very interesting at
this moment. 'InitPC' is interesting though. Let's try :
< u 07EAA7D8 <enter>
or
< unasm 07EAA7D8 <enter>
> 07EAA7D8: 7200 MOVEQ.L #0,D1
> 07EAA7DA: 7064 MOVEQ.L #$64,D0
> 07EAA7DC: 5281 ADDQ.L #1,D1
> 07EAA7DE: 51C8 FFFC DBF D0,$7EAA7DC
> 07EAA7E2: 6100 0010 BSR $7EAA7F4
> 07EAA7E6: 6708 BEQ $7EAA7F0
> 07EAA7E8: 6100 0022 BSR $7EAA80C
> 07EAA7EC: 6100 0046 BSR $7EAA834
> 07EAA7F0: 7000 MOVEQ.L #0,D0
> 07EAA7F2: 4E75 RTS
> 07EAA7F4: 203C 0000 0064 MOVE.L #$64,D0
> 07EAA7FA: 7200 MOVEQ.L #0,D1
> 07EAA7FC: 2C78 0004 MOVEA.L (4),A6
> 07EAA800: 4EAE FF3A JSR ($FF3A,A6)
> 07EAA804: 41FA 0042 LEA ($7EAA848,PC),A0
> 07EAA808: 2080 MOVE.L D0,(A0)
> 07EAA80A: 4E75 RTS
> 07EAA80C: 7000 MOVEQ.L #0,D0
> 07EAA80E: 7201 MOVEQ.L #1,D1
> 07EAA810: 7402 MOVEQ.L #2,D2
Well, this is our program. But there are ²symbol hunks in our program.
'debug n' does not automatically load them ('debug l' does, but this command
will be explained later). You can ²load symbols with the 'symbol' command :
< symbol l examples/buggyprogram <enter>
< u 07EAA7D8 <enter>
> StartProgr7200 MOVEQ.L #0,D1
> 07EAA7DA: 7064 MOVEQ.L #$64,D0
> loop 5281 ADDQ.L #1,D1
> 07EAA7DE: 51C8 FFFC DBF D0,loop
> 07EAA7E2: 6100 0010 BSR Sub1
> 07EAA7E6: 6708 BEQ theend
> 07EAA7E8: 6100 0022 BSR Sub2
> 07EAA7EC: 6100 0046 BSR Sub3
> theend 7000 MOVEQ.L #0,D0
> 07EAA7F2: 4E75 RTS
> Sub1 203C 0000 0064 MOVE.L #$64,D0
> 07EAA7FA: 7200 MOVEQ.L #0,D1
> 07EAA7FC: 2C78 0004 MOVEA.L (4),A6
> 07EAA800: 4EAE FF3A JSR ($FF3A,A6)
> 07EAA804: 41FA 0042 LEA (Block,PC),A0
> 07EAA808: 2080 MOVE.L D0,(A0)
> 07EAA80A: 4E75 RTS
> Sub2 7000 MOVEQ.L #0,D0
> 07EAA80E: 7201 MOVEQ.L #1,D1
> 07EAA810: 7402 MOVEQ.L #2,D2
You can ³show all symbols with the 'symbol' command :
< symbol s <enter>
> StartProgram : 07EAA7D8 , 132818904
> loop : 07EAA7DC , 132818908
> theend : 07EAA7F0 , 132818928
> Sub1 : 07EAA7F4 , 132818932
> Sub2 : 07EAA80C , 132818956
> Sub3 : 07EAA834 , 132818996
> Block : 07EAA848 , 132819016
The two values on the right of each symbol are the same. The only difference
is that the left one is ¹hexadecimal and the right one is ¹decimal.
Because we have loaded the symbols for the current debug task we can use the
symbols in ¹expressions. Here are some examples :
Disassemble 5 instructions starting with 'StartProgram' (note that symbols
are case sensitive) :
< u StartProgram 5 <enter>
> StartProgr7200 MOVEQ.L #0,D1
> 07EAA7DA: 7064 MOVEQ.L #$64,D0
> loop 5281 ADDQ.L #1,D1
> 07EAA7DE: 51C8 FFFC DBF D0,loop
> 07EAA7E2: 6100 0010 BSR Sub1
Show the distance between subroutine 2 and subroutine 1 :
< d Sub2-Sub1 <enter>
> 00000018,24
You can do many other things with the 'symbol' command but 'symbol l' and
'symbol s' are sufficient at this moment.
There is still one thing we should do :
< loadfd exec fd:exec_lib.fd <enter>
With this command PowerVisor loads all the ³library function definitions in
memory. That way PowerVisor will know how to show a ²library function when
one is encountered while ¹tracing. You do not have to load ¹fd-files, but
it is certainly very easy. I have the four big fd-files ('exec.library',
'graphics.library', 'intuition.library' and 'dos.library') permanently
loaded in memory (I have put four 'loadfd' commands in the
¹s:PowerVisor-startup file).
----------------------------------Tracing ------------------------------------
Now we can start tracing :
< trace i <enter>
or
< tr i <enter>
> ---------------------------------------------------------------------------
> D0: 00000001 D1: 01FAA9F5 D2: 00002EE0 D3: 07ED3A1C
> D4: 00000001 D5: 00000001 D6: 01FAA08F D7: 07EAA7D4
> A0: 07ED3A1C A1: 07EF9D28 A2: 07E0CEA4 A3: 07EAA7D4
> A4: 07EFCC00 A5: 00F906DE A6: 00F906D2
> PC: 07EAA7D8 SP: 07EFCBFC SR: 0010
> 00000000: 0000 0000 ORI.B #0,D0
>
> StartProgr7200 MOVEQ.L #0,D1
> 07EAA7DA: 7064 MOVEQ.L #$64,D0
> loop 5281 ADDQ.L #1,D1
> 07EAA7DE: 51C8 FFFC DBF D0,loop
> 07EAA7E2: 6100 0010 BSR Sub1
(tr i : give 'I'nformation)
This command shows where we are. No actual tracing is done. The registers
are shown and the five first instructions. The program counter points to the
second instruction in this output. The first instruction is always equal to
the previous executed instruction. Initially it is initialized to address 0.
Note that you can change the format of this output with the 'prefs dmode'
and 'prefs debug' commands (See the 'InstallingPowerVisor' file).
Now we are really going to trace one instruction :
< tr <enter>
> ---------------------------------------------------------------------------
> D0: 00000001 D1: 00000000 D2: 00002EE0 D3: 07ED3A1C
> D4: 00000001 D5: 00000001 D6: 01FAA08F D7: 07EAA7D4
> A0: 07ED3A1C A1: 07EF9D28 A2: 07E0CEA4 A3: 07EAA7D4
> A4: 07EFCC00 A5: 00F906DE A6: 00F906D2
> PC: 07EAA7DA SP: 07EFCBFC SR: 0014
> StartProgr7200 MOVEQ.L #0,D1
>
> 07EAA7DA: 7064 MOVEQ.L #$64,D0
> loop 5281 ADDQ.L #1,D1
> 07EAA7DE: 51C8 FFFC DBF D0,loop
> 07EAA7E2: 6100 0010 BSR Sub1
> 07EAA7E6: 6708 BEQ theend
In the register display you can see that 'd1' now has the value 0.
'StartProgr' is now the previous instruction. The programcounter now points
to the instruction 'moveq.l #$64,d0'.
Trace six instructions at once :
< tr n 6 <enter>
> ---------------------------------------------------------------------------
> D0: 00000062 D1: 00000003 D2: 00002EE0 D3: 07ED3A1C
> D4: 00000001 D5: 00000001 D6: 01FAA08F D7: 07EAA7D4
> A0: 07ED3A1C A1: 07EF9D28 A2: 07E0CEA4 A3: 07EAA7D4
> A4: 07EFCC00 A5: 00F906DE A6: 00F906D2
> PC: 07EAA7DE SP: 07EFCBFC SR: 0000
> loop 5281 ADDQ.L #1,D1
>
> 07EAA7DE: 51C8 FFFC DBF D0,loop
> 07EAA7E2: 6100 0010 BSR Sub1
> 07EAA7E6: 6708 BEQ theend
> 07EAA7E8: 6100 0022 BSR Sub2
> 07EAA7EC: 6100 0046 BSR Sub3
(tr n : trace 'N'umber instruction)
We are now in the loop.
To ²step over the loop we can use the following instruction :
< tr o <enter>
> ---------------------------------------------------------------------------
> D0: 0000FFFF D1: 00000065 D2: 00002EE0 D3: 07ED3A1C
> D4: 00000001 D5: 00000001 D6: 01FAA08F D7: 07EAA7D4
> A0: 07ED3A1C A1: 07EF9D28 A2: 07E0CEA4 A3: 07EAA7D4
> A4: 07EFCC00 A5: 00F906DE A6: 00F906D2
> PC: 07EAA7E2 SP: 07EFCBFC SR: 0000
> 07EAA7DE: 51C8 FFFC DBF D0,loop
>
> 07EAA7E2: 6100 0010 BSR Sub1
> 07EAA7E6: 6708 BEQ theend
> 07EAA7E8: 6100 0022 BSR Sub2
> 07EAA7EC: 6100 0046 BSR Sub3
> theend 7000 MOVEQ.L #0,D0
> Breakpoint...
(tr o : trace 'O'ver)
'tr o' places a ¹breakpoint after the current instruction and then executes
until the breakpoint is encountered. You can trace over every instruction
with this command, but you can't use it in ¹ROM-code since PowerVisor can't
put a ³breakpoint in ROM (don't worry ! there are solutions to this problem,
we will see them later on).
We step into the subroutine 'Sub1' with :
< tr <enter>
> ---------------------------------------------------------------------------
> D0: 0000FFFF D1: 00000065 D2: 00002EE0 D3: 07ED3A1C
> D4: 00000001 D5: 00000001 D6: 01FAA08F D7: 07EAA7D4
> A0: 07ED3A1C A1: 07EF9D28 A2: 07E0CEA4 A3: 07EAA7D4
> A4: 07EFCC00 A5: 00F906DE A6: 00F906D2
> PC: 07EAA7F4 SP: 07EFCBF8 SR: 0000
> 07EAA7E2: 6100 0010 BSR Sub1
>
> Sub1 203C 0000 0064 MOVE.L #$64,D0
> 07EAA7FA: 7200 MOVEQ.L #0,D1
> 07EAA7FC: 2C78 0004 MOVEA.L (4),A6
> 07EAA800: 4EAE FF3A JSR ($FF3A,A6)
> 07EAA804: 41FA 0042 LEA (Block,PC),A0
Trace another three instructions :
< tr n 3 <enter>
> ---------------------------------------------------------------------------
> D0: 00000064 D1: 00000000 D2: 00002EE0 D3: 07ED3A1C
> D4: 00000001 D5: 00000001 D6: 01FAA08F D7: 07EAA7D4
> A0: 07ED3A1C A1: 07EF9D28 A2: 07E0CEA4 A3: 07EAA7D4
> A4: 07EFCC00 A5: 00F906DE A6: 07E007D8
> PC: 07EAA800 SP: 07EFCBF8 SR: 0004
> 07EAA7FC: 2C78 0004 MOVEA.L (4),A6
>
> 07EAA800: 4EAE FF3A JSR (AllocMem,A6)
> 07EAA804: 41FA 0042 LEA (Block,PC),A0
> 07EAA808: 2080 MOVE.L D0,(A0)
> 07EAA80A: 4E75 RTS
> Sub2 7000 MOVEQ.L #0,D0
Thanks to the loaded fd-file you can now see that this function is actually
the Exec AllocMem. We do not want to run through the complete rom function
so we ²trace over the call with :
< tr t <enter>
> ---------------------------------------------------------------------------
> D0: 07EFCE90 D1: 00002F48 D2: 00002EE0 D3: 07ED3A1C
> D4: 00000001 D5: 00000001 D6: 01FAA08F D7: 07EAA7D4
> A0: 07E00000 A1: 07EFCE90 A2: 07E0CEA4 A3: 07EAA7D4
> A4: 07EFCC00 A5: 00F906DE A6: 07E007D8
> PC: 07EAA804 SP: 07EFCBF8 SR: 0010
> 07EAA800: 4EAE FF3A JSR ($FF3A,A6)
>
> 07EAA804: 41FA 0042 LEA (Block,PC),A0
> 07EAA808: 2080 MOVE.L D0,(A0)
> 07EAA80A: 4E75 RTS
> Sub2 7000 MOVEQ.L #0,D0
> 07EAA80E: 7201 MOVEQ.L #1,D1
> Breakpoint...
(tr t : 'T'race over BSR or JSR sorry, couldn't find a better character)
'tr t' looks similar to 'tr o'. The big difference is that 'tr t' works only
for 'BSR' and 'JSR' instructions. And what is more important : 'tr t' works
in ¹ROM-code. If 'tr t' is used for an instruction other than 'BSR' or 'JSR'
it is analogous to 'tr' (simple singlestep).
We can see that the AllocMem function had success (I hope this is really the
case) because 'd0' contains the address of the newly allocated memory.
We continue tracing until the next ³change of program flow happens :
< tr b <enter>
> ---------------------------------------------------------------------------
> D0: 07EFCE90 D1: 00002F48 D2: 00002EE0 D3: 07ED3A1C
> D4: 00000001 D5: 00000001 D6: 01FAA08F D7: 07EAA7D4
> A0: 07EAA848 A1: 07EFCE90 A2: 07E0CEA4 A3: 07EAA7D4
> A4: 07EFCC00 A5: 00F906DE A6: 07E007D8
> PC: 07EAA80A SP: 07EFCBF8 SR: 0010
> 07EAA808: 2080 MOVE.L D0,(A0)
>
> 07EAA80A: 4E75 RTS
> Sub2 7000 MOVEQ.L #0,D0
> 07EAA80E: 7201 MOVEQ.L #1,D1
> 07EAA810: 7402 MOVEQ.L #2,D2
> 07EAA812: 7603 MOVEQ.L #3,D3
(tr b : trace until 'B'ranch)
'tr b' traces until a change of program control happens. This means that
tracing will stop always at the following instructions :
JMP
JSR
BRA
BSR
RTE
RTD
RTR
RTS
TRAP
and tracing will stop at the following instructions if the brach would
succeed :
Bcc
DBcc
Go out this subroutine :
< tr <enter>
> ---------------------------------------------------------------------------
> D0: 07EFCE90 D1: 00002F48 D2: 00002EE0 D3: 07ED3A1C
> D4: 00000001 D5: 00000001 D6: 01FAA08F D7: 07EAA7D4
> A0: 07EAA848 A1: 07EFCE90 A2: 07E0CEA4 A3: 07EAA7D4
> A4: 07EFCC00 A5: 00F906DE A6: 07E007D8
> PC: 07EAA7E6 SP: 07EFCBFC SR: 0010
> 07EAA80A: 4E75 RTS
>
> 07EAA7E6: 6708 BEQ theend
> 07EAA7E8: 6100 0022 BSR Sub2
> 07EAA7EC: 6100 0046 BSR Sub3
> theend 7000 MOVEQ.L #0,D0
> 07EAA7F2: 4E75 RTS
< tr <enter>
> ---------------------------------------------------------------------------
> D0: 07EFCE90 D1: 00002F48 D2: 00002EE0 D3: 07ED3A1C
> D4: 00000001 D5: 00000001 D6: 01FAA08F D7: 07EAA7D4
> A0: 07EAA848 A1: 07EFCE90 A2: 07E0CEA4 A3: 07EAA7D4
> A4: 07EFCC00 A5: 00F906DE A6: 07E007D8
> PC: 07EAA7E8 SP: 07EFCBFC SR: 0010
> 07EAA7E6: 6708 BEQ theend
>
> 07EAA7E8: 6100 0022 BSR Sub2
> 07EAA7EC: 6100 0046 BSR Sub3
> theend 7000 MOVEQ.L #0,D0
> 07EAA7F2: 4E75 RTS
> Sub1 203C 0000 0064 MOVE.L #$64,D0
We suspect nothing bad in 'Sub2' so we simply trace over it :
< tr t <enter>
> ---------------------------------------------------------------------------
> D0: 00000000 D1: 00000001 D2: 00000002 D3: 00000003
> D4: 00000004 D5: 00000005 D6: 00000006 D7: 00000007
> A0: 07EFCE90 A1: 07EFCE90 A2: 07E0CEA4 A3: 07EAA7D4
> A4: 07EFCC00 A5: 00F906DE A6: 07E007D8
> PC: 07EAA820 SP: 07EFCBF8 SR: 0010
> 07EAA7E8: 6100 0022 BSR Sub2
>
> 07EAA820: 4AFC ILLEGAL
> 07EAA822: 20C0 MOVE.L D0,(A0)+
> 07EAA824: 20C1 MOVE.L D1,(A0)+
> 07EAA826: 20C2 MOVE.L D2,(A0)+
> 07EAA828: 20C3 MOVE.L D3,(A0)+
> Illegal instruction !
There is something wrong ! This is called a ²²resident breakpoint. You can
put resident breakpoints in a program using the ²'ILLEGAL' instruction.
PowerVisor will automatically stop at such places.
²Skip over the instruction with :
< tr s <enter>
> ---------------------------------------------------------------------------
> D0: 00000000 D1: 00000001 D2: 00000002 D3: 00000003
> D4: 00000004 D5: 00000005 D6: 00000006 D7: 00000007
> A0: 07EFCE90 A1: 07EFCE90 A2: 07E0CEA4 A3: 07EAA7D4
> A4: 07EFCC00 A5: 00F906DE A6: 07E007D8
> PC: 07EAA820 SP: 07EFCBF8 SR: 0010
> 07EAA820: 4AFC ILLEGAL
>
> 07EAA822: 20C0 MOVE.L D0,(A0)+
> 07EAA824: 20C1 MOVE.L D1,(A0)+
> 07EAA826: 20C2 MOVE.L D2,(A0)+
> 07EAA828: 20C3 MOVE.L D3,(A0)+
> 07EAA82A: 20C4 MOVE.L D4,(A0)+
('tr s' : 'S'kip instruction)
Now we have something special. Since we used the 'tr t' command to trace
over the subroutine 'Sub2' we have created a breakpoint after the 'BSR Sub2'
instruction. But if we would look after the 'BSR Sub2' instruction we
would find no breakpoint (we will see later how PowerVisor shows breakpoints
in the disassembly display). This is because the 'tr t' command works in
a special way to make sure that you can use it in ROM-code too. Here follows
an explanation of what has happened :
You typed 'tr t' when to skip 'BSR Sub2' a time ago.
PowerVisor performs a 'tr' to trace the 'BSR' instruction.
Now the top of the stack contains the returnaddress for the 'BSR'
instruction, this is the address of the instruction after 'BSR Sub2'.
PowerVisor replaces the address on the stack with another address.
This address points to a ²private breakpoint. Since this private
breakpoint is always in RAM, there is no problem setting this
breakpoint. When the subroutine returns (with 'RTS') later on (this
has not happened at this moment), it will not return to the instruction
after the 'BSR' but to the breakpoint in RAM. PowerVisor will trap this
and set the programcounter of the task to the right address: this is
the instruction after the 'BSR Sub2'.
It would be different if you hade used 'tr o' instead of 'tr t'. 'tr o'
would put a breakpoint directly after the 'BSR Sub2'. This will
ofcourse not work if the 'BSR' is in ROM since a breakpoint is in fact
an ILLEGAL instruction.
But since the routine 'Sub2' was interrupted (the 'ILLEGAL' instruction
caused this). The private breakpoint has not been encountered yet and
the value on the stack is still the wrong value. We can make use of this
feature and simply continue the 'tr t' where it left of with :
< tr g <enter>
> ---------------------------------------------------------------------------
> D0: 00000000 D1: 00000001 D2: 00000002 D3: 00000003
> D4: 00000004 D5: 00000005 D6: 00000006 D7: 00000007
> A0: 07EFCEB0 A1: 07EFCE90 A2: 07E0CEA4 A3: 07EAA7D4
> A4: 07EFCC00 A5: 00F906DE A6: 07E007D8
> PC: 07EAA7EC SP: 07EFCBFC SR: 0010
> 07EAA822: 20C0 MOVE.L D0,(A0)+
>
> 07EAA7EC: 6100 0046 BSR Sub3
> theend 7000 MOVEQ.L #0,D0
> 07EAA7F2: 4E75 RTS
> Sub1 203C 0000 0064 MOVE.L #$64,D0
> 07EAA7FA: 7200 MOVEQ.L #0,D1
> Breakpoint...
('tr g' : trace 'G'o)
The 'tr g' command simply executes the program until a breakpoint is
encountered.
Note that it would make no difference if you would trace the program step
by step. At one moment you would encounter the ²private breakpoint. Simply
tracing over this breakpoint will return to the correct place in the
program.
We step into 'Sub3' :
< tr <enter>
> ---------------------------------------------------------------------------
> D0: 00000000 D1: 00000001 D2: 00000002 D3: 00000003
> D4: 00000004 D5: 00000005 D6: 00000006 D7: 00000007
> A0: 07EFCEB0 A1: 07EFCE90 A2: 07E0CEA4 A3: 07EAA7D4
> A4: 07EFCC00 A5: 00F906DE A6: 07E007D8
> PC: 07EAA834 SP: 07EFCBF8 SR: 0010
> 07EAA7EC: 6100 0046 BSR Sub3
>
> Sub3 203C 0000 0040 MOVE.L #$40,D0
> 07EAA83A: 227A 000C MOVEA.L (Block,PC),A1
> 07EAA83E: 2C78 0004 MOVEA.L (4),A6
> 07EAA842: 4EAE FF2E JSR ($FF2E,A6)
> 07EAA846: 4E75 RTS
< tr <enter>
> ---------------------------------------------------------------------------
> D0: 00000040 D1: 00000001 D2: 00000002 D3: 00000003
> D4: 00000004 D5: 00000005 D6: 00000006 D7: 00000007
> A0: 07EFCEB0 A1: 07EFCE90 A2: 07E0CEA4 A3: 07EAA7D4
> A4: 07EFCC00 A5: 00F906DE A6: 07E007D8
> PC: 07EAA83A SP: 07EFCBF8 SR: 0010
> Sub3 203C 0000 0040 MOVE.L #$40,D0
>
> 07EAA83A: 227A 000C MOVEA.L (Block,PC),A1
> 07EAA83E: 2C78 0004 MOVEA.L (4),A6
> 07EAA842: 4EAE FF2E JSR ($FF2E,A6)
> 07EAA846: 4E75 RTS
> Block 07EF CE90 BSET D3,($CE90,A7)
We see that something is wrong. We have allocated 100 bytes of memory
($64) but we are only going to free 64 bytes ($40). This is clearly
a bug and should be fixed. But to prevent memoryloss we are going to
continue anyway. We simply change the 'd0' ¹register :
< d @d0 <enter>
> 00000040,64
< @d0=100 <enter>
You see how we can look at registers and change their values.
We are not interested in the rest of the program. We simply let it go :
< tr g <enter>
> Program quits !
The program has stopped.
Some important 'trace' commands have been explained. There are a lot more.
Some of the other 'trace' commands will be used in the following example.
You have to refer to the online help or to the 'CommandReference' file for
the other features.
============================ Example session 2 ===============================
I have prepared another program so you can see the power of PowerVisor.
With this example we are going to make you used to breakpoints and some
other advanced features of the 'trace' command. We are also going to use
the ²²fullscreen debugger (Note that I will explain later how you can
customize this fullscreen debugger to your wishes and how you can use
the 'db' script to do this for you).
-------------------------- Starting the session ------------------------------
We are now going to load the program using 'debug l'. Normally this is
not the recommended way since this instruction does not perfectly emulate
a ¹Cli or ¹WorkBench. But this does not matter for our little program.
Note that the AmigaDOS 2.0 version of PowerVisor perfectly creates a CLI,
so 'debug l' is a very good way to load a program if you have AmigaDOS 2.0.
< debug l examples/buggyprogram2 <enter>
The symbols are automatically loaded by 'debug l' :
< symbol s <enter>
> StartProgram : 07EADCC0 , 132832448
> Long : 07EADCCE , 132832462
> recur : 07EADCE0 , 132832480
> theend : 07EADCEC , 132832492
Open the fullscreen debugger display :
< dwin <enter>
< prefs dmode n <enter>
The 'prefs dmode' command is used to disable the output on the 'Main'
logical window you normally get after each trace. All the output goes
automatically to the 'Debug' logical window if it is open (but if you
set 'prefs dmode f' as it is default you will get output in the 'Debug'
logical window AND on the 'Main' logical window. This is probably not
as intended).
Drag the ²horizontal bar between the ³'Main' logical window and the
³'Debug' logical window until all the five instructions of the disassembly
are visible.
The following ¹keys can be used :
<ctrl>+<NumPad Up> to decrease the top visible instruction address
with 2
<ctrl>+<NumPad Down> to increase this address with 2
<ctrl>+<NumPad PgUp> to decrease this address with 20
<ctrl>+<NumPad PgDn> to increase this address with 20
<ctrl>+<NumPad 5> to set this address equal to the programcounter
Using this keys you can ¹scroll through your code (try it).
Press :
< <ctrl>+<NumPad 5>
To go back to the programcounter.
(Note that you can also use the 'dscroll' and 'dstart' commands to scroll
through your program, see the 'CommandReference' file)
The fullscreen debugger display looks almost the same as the output from
the 'trace' command in the earlier section. The differences are :
- There is an indicator of what the task is doing.
NONE the task is waiting for PowerVisor instructions
TRACE the task is tracing
EXEC the task is executing
- The top instruction (except for the previous instruction indicator)
is not always equal to the instruction at the ¹programcounter. The
programcounter is indicated by ²hilighted line.
- The ²previous instruction is only updated when the programcounter makes
a jump out of the current displayed instructions.
------------------------------- Breakpoints ----------------------------------
First a simple ¹¹breakpoint :
Lets put a breakpoint in the 'Long' subroutine.
< u Long <enter>
> Long 7000 MOVEQ.L #0,D0
> 07EADCD0: 7201 MOVEQ.L #1,D1
> 07EADCD2: 7402 MOVEQ.L #2,D2
> 07EADCD4: 7603 MOVEQ.L #3,D3
> 07EADCD6: 7804 MOVEQ.L #4,D4
> 07EADCD8: 7A05 MOVEQ.L #5,D5
> 07EADCDA: 7C06 MOVEQ.L #6,D6
> 07EADCDC: 7E07 MOVEQ.L #7,D7
> 07EADCDE: 4E75 RTS
> recur 5280 ADDQ.L #1,D0
> 07EADCE2: 0C80 0000 00C8 CMPI.L #$C8,D0
> 07EADCE8: 6E02 BGT theend
> 07EADCEA: 61F4 BSR recur
> theend 4E75 RTS
> 07EADCEE: 0000 07EA ORI.B #$EA,D0
> 07EADCF2: DD08 ADDX.B -(A0),-(A6)
> 07EADCF4: 0000 0000 ORI.B #0,D0
> 07EADCF8: 07E2 BSET D3,-(A2)
> 07EADCFA: 68A0 BVC $7EADC9C
> 07EADCFC: 0002 004C ORI.B #$4C,D2
< break n 07EADCD2 <enter> (Note ! Use the equivalent address!)
or
< b n 07EADCD2 <enter>
> 00000001,1
('b n' : 'N'ormal breakpoint)
The output from this command is the breakpoint number. PowerVisor can have
as many breakpoints as memory permits. Breakpoints are always refered to
with their number.
With the 'info' command you can now ask more information about the
breakpoints :
< l dbug <enter>
> Debug task : Node Task InitPC TD ID Mode SMode TMode
> ---------------------------------------------------------------------------
> examples/buggyprogra: 07EADB90 07ED5840 07EADCC0 FF FF NONE WAIT NORM
< info dbug:'examples/buggyprogram2' dbug <enter>
or
< i db:examp db <enter>
> Debug task : Node Task InitPC TD ID Mode SMode TMode
> ---------------------------------------------------------------------------
> examples/buggyprogra: 07EADB90 07ED5840 07EADCC0 FF FF NONE WAIT NORM
>
> Node Number Where UsageCnt Type Condition
> ---------------------------------------------------------------------------
> 07EBA168 1 07EADCD2 0 N
We can see that there is one breakpoint defined with number 1 and position
07EA77DA. It has not been used yet and it is a normal (N) breakpoint.
('Condition' is explained later).
Lets have a look at the disassembly :
< u Long 20 <enter>
> Long 7000 MOVEQ.L #0,D0
> 07EADCD0: 7201 MOVEQ.L #1,D1
> 07EADCD2: 4AFC MOVEQ.L #2,D2 >1
> 07EADCD4: 7603 MOVEQ.L #3,D3
> 07EADCD6: 7804 MOVEQ.L #4,D4
> 07EADCD8: 7A05 MOVEQ.L #5,D5
> 07EADCDA: 7C06 MOVEQ.L #6,D6
> 07EADCDC: 7E07 MOVEQ.L #7,D7
> 07EADCDE: 4E75 RTS
> recur 5280 ADDQ.L #1,D0
> 07EADCE2: 0C80 0000 00C8 CMPI.L #$C8,D0
> 07EADCE8: 6E02 BGT theend
> 07EADCEA: 61F4 BSR recur
> theend 4E75 RTS
> 07EADCEE: 0000 07EA ORI.B #$EA,D0
> 07EADCF2: DD08 ADDX.B -(A0),-(A6)
> 07EADCF4: 0000 0000 ORI.B #0,D0
> 07EADCF8: 07E2 BSET D3,-(A2)
> 07EADCFA: 68A0 BVC $7EADC9C
> 07EADCFC: 0002 004C ORI.B #$4C,D2
The breakpoint is the instruction with the '>1' appended.
Now we start the program and see where it ends :
< tr g <enter>
> Breakpoint...
(Notice that we no longer get the complete output on 'Main'. All output
is in the 'Debug' logical window)
The breakpoint has been encountered. Since it is a normal breakpoint it
is not removed.
< i db:examp db <enter>
> Debug task : Node Task InitPC TD ID Mode SMode TMode
> ---------------------------------------------------------------------------
> examples/buggyprogra: 07EADB90 07ED5840 07EADCC0 FF FF NONE WAIT NORM
>
> Node Number Where UsageCnt Type Condition
> ---------------------------------------------------------------------------
> 07EBA168 1 07EADCD2 1 N
Now we see that the ²usage counter has incremented.
We make two new breakpoints :
< b t 07EADCDA <enter>
< b c recur '@d0==100' <enter>
< i db:exam db <enter>
> Debug task : Node Task InitPC TD ID Mode SMode TMode
> ---------------------------------------------------------------------------
> examples/buggyprogra: 07EADB90 07ED5840 07EADCC0 FF FF NONE WAIT STEP
>
> Node Number Where UsageCnt Type Condition
> ---------------------------------------------------------------------------
> 07EBA288 3 07EADCE0 0 C @d0==100
> 07EB5B60 2 07EADCDA 0 T
> 07EBA168 1 07EADCD2 1 N
('b t' : 'T'emporary breakpoint)
('b c' : 'C'onditional breakpoint)
'b t' makes a ²temporary breakpoint. This is a breakpoint that only breaks
once. 'b c' makes a conditional breakpoint. ²Conditional breakpoints are
very powerful as you will see in the following demonstration.
< tr g <enter>
> Breakpoint...
The breakpoint breaks and is immediatelly removed.
< tr g <enter>
> Breakpoint...
The conditional breakpoint breaks because 'd0' is equal to 100. A conditional
breakpoint is a very powerful way to control your program. The breakpoint
condition can be as complex as you wish (with the exception that you can't
use the ²group operator) and you can refer to all ¹registers like @pc, @sr,
@sp, @d0 to @d7 and @a0 to @a6.
We ³remove the breakpoint with :
< b r 3 <enter>
('b r' : 'R'emove breakpoint)
Now we are going to put a breakpoint just after the 'BSR' instruction :
< u StartProgram <enter>
> StartProgr6100 000C BSR Long
> 07EADCC4: 7000 MOVEQ.L #0,D0
> 07EADCC6: 6100 0018 BSR recur
> 07EADCCA: 7000 MOVEQ.L #0,D0
> 07EADCCC: 4E75 RTS
> Long 7000 MOVEQ.L #0,D0
> 07EADCD0: 7201 MOVEQ.L #1,D1
> 07EADCD2: 4AFC MOVEQ.L #2,D2 >1
> 07EADCD4: 7603 MOVEQ.L #3,D3
> 07EADCD6: 7804 MOVEQ.L #4,D4
> 07EADCD8: 7A05 MOVEQ.L #5,D5
> 07EADCDA: 7C06 MOVEQ.L #6,D6
> 07EADCDC: 7E07 MOVEQ.L #7,D7
> 07EADCDE: 4E75 RTS
> recur 5280 ADDQ.L #1,D0
> 07EADCE2: 0C80 0000 00C8 CMPI.L #$C8,D0
> 07EADCE8: 6E02 BGT theend
> 07EADCEA: 61F4 BSR recur
> theend 4E75 RTS
> 07EADCEE: 0000 07EA ORI.B #$EA,D0
We see that there is still another breakpoint present in the 'Long'
subroutine. Remove it with :
< b r 1 <enter>
We make the new breakpoint :
< b n 07EADCCA <enter>
> 00000001,1
Now we execute until we reach that breakpoint :
< tr g <enter>
> Breakpoint...
And we start all over again by setting the programcounter back to the
start of the program :
< @pc=StartProgram <enter>
Now we are ready to demonstrate yet another powerful feature which looks a
bit like conditional breakpoints : ²conditional tracing.
< tr c '@d0==100' <enter>
('tr c' : 'C'onditional tracing)
'tr c' singlesteps the program until the condition is true. The difference
with the conditional breakpoint is that the breakpoint only checks the
condition when the breakpoint is passed. With conditional tracing the
condition is checked after each instruction. Conditional tracing is ofcourse
much slower.
Remove the debug task from memory with :
< debug u <enter>
This command removes all breakpoints and unloads the program. It is best
to always use this command in conjunction with 'debug l'. You can also
use 'debug r' to remove all breakpoints and stop debugging. After 'debug r'
the debug program will simply continue as if nothing has happened. This
has two disadvantages : It is possible that the program is buggy and will
crash. In that case it is not wise to use 'debug r'. PowerVisor will also
not be able to unload the program from memory. This means that you
will loose some memory (you == your Amiga :-)
'debug r' is more useful in conjunction with the 'debug n' command (and
also with the 'debug c' command which can be used to catch a task).
You can also use 'debug f' (see the 'CommandReference' file for more
info).
Close the debug logical window with :
< dwin <enter>
=============================== Some theory ==================================
When you issue a trace command to PowerVisor, the 'trace' command will return
immediatelly. This means that when the trace could take a long time, you will
still be able to use PowerVisor for other commands. For example, when you are
tracing conditionally, PowerVisor will do absolutely nothing. The debug task
does everything until the condition becomes true. The debug task will then
send a signal to PowerVisor and PowerVisor will update the debug display.
The conditional trace command is one of the trace commands that uses
²singlestep mode for tracing. This is slow but sometimes the only way to
trace something. The 'go' trace command ('tr g') is another trace command.
This trace command uses ²execute mode for tracing. The task runs at full
speed until a breakpoint is encountered. It is possible that you want
singlestep mode for the 'tr g' command too. For example, you could use this
to see how a program runs. Since the program runs a bit slower you will be
able to see much better what happens at each step. To use singlestep mode
with the 'tr g' command you must use 'tr gt' ('t' for trace). Most tracing
commands have these two versions.
Note that you can ³interrupt the tracing if you like with 'tr h' or
'tr f'.
Some commands (like 'tr u' and 'tr o' (explained later)) make a private
breakpoint. A ²private breakpoint is a breakpoint with number 0. This
breakpoint is automatically cleared when another breakpoint with number 0
is about to be created, or when the breakpoint breaks.
========================== Resident breakpoints ==============================
You can set ²²resident breakpoints in your programs by including an 'ILLEGAL'
instruction at the right place. When you want to use them you must make sure
that PowerVisor is started and that you use 'mode patch'. Otherwise the
results will not be very satisfactory. After that you simply start your
program (from the 'Shell' or 'Workbench') (Note ! Don't use 'debug n'
in PowerVisor). When the program collides with the resident breakpoint,
PowerVisor will ³trap the crash. You have now made a ²crash node. You can
than use 'debug t' with the crash node or with the crashed task to
start debugging at the 'ILLEGAL' instruction.
=========================== The fdebug command ===============================
To make life easier s/PowerVisor-startup defines an alias that you can
use to initialize the fullscreen debugger. This alias uses the 'db'
script to open the debug logical window and to initialize some
keys. See the 'Aliases' reference file for more information about the
'fdebug' alias.
========================== sourcelevel debugging =============================
If you want you can load the source for the debug task you are tracing.
PowerVisor will automatically follow this source, even when you switch
to a routine in another file. See the 'Source' command in the
'CommandReference' file for more information.
=========================== The other commands ===============================
Here follows a summary of what you can do with all debug commands :
break n <address> Set 'N'ormal breakpoint.
The breakpoint is not removed after breaking
break t <address> Set 'T'emporary breakpoint.
The breakpoint is removed after breaking
break p <address> Set 'P'rofile breakpoint
This breakpoint never breaks. It only increments
the usagecounter. You can use it to see if a
certain routine is much used
break a <address> <timeout>
Break 'A'fter <timeout> passes.
The breakpoint is removed after breaking
break c <address> <condition>
'C'onditional breakpoint. This breakpoint breaks when
the condition is true. The breakpoint is not removed
after breaking
break r <breakpoint number>
Remove a breakpoint
debug n Wait for 'N'ext prorgram
debug c Wait for next task
debug l <filename> 'L'oad a program and load symbols
This command also creates a CLI structure if you
use the AmigaDOS 2.0 version of PowerVisor
debug t <task>|<crash node>
Take an existing task or crash node and make
a debug node for it. With this command you can
in theory debug any task in the system (be
careful though)
debug f Remove the current debug node and freeze the
debug task
debug f <debug node> Remove the specified debug node and freeze the
corresponding task. Use this command if you are
debugging multiple programs at the same time.
You can find all debug nodes in the 'dbug' list
debug r Remove the current debug node. The debug task
will continue executing at the programcounter
debug r <debug node> Remove the specified debug node
debug u Remove the current debug node. The debug task
will be stopped and the program will be unloaded.
debug u <debug node> Same as 'debug u' but for a specified debug node.
debug d <name> Create a ³dummy debug node with name <name>.
You can't use this node for debugging but you
can use it to create ¹symbols
drefresh Refresh the ²debug display
dscroll <offset> Scroll <offset> bytes up in the fullscreen debugger.
Negative values are allowed. <offset> will be made
a multiple of two.
dstart <address> Set the start of the debug logical window.
duse <debug node> Set the default debug node. This is useful when
you are debugging multiple tasks at the same time.
dwin Open/Close 'Debug' logical window
symbol l <filename> [<hunkaddress>]
³Load the symbols for the current debug task.
If you give <hunkaddress>, PowerVisor will load the
symbols for the given ¹hunks. This is extremely
useful when you have created a ³dummy debug task.
Note that <hunkaddress> is 4 more than the number
given in the hunklist with the 'hunks' command.
Note that <hunkaddress> is not optional when you are
loading symbols for a dummy debug task.
symbol c Clear all symbols for the current debug node
symbol a <symbolname> <value>
Add a symbol to the list of symbols
symbol r <symbolname>
Remove a symbol from the list of symbols
symbol s List all symbols for the current debug node
source l <filename> [<hunkaddress>]
³Load the source for the current debug task.
If you give <hunkaddress>, PowerVisor will load the
source for the given ¹hunks. This is extremely
useful when you have created a ³dummy debug task.
Note that <hunkaddress> is 4 more than the number
given in the hunklist with the 'hunks' command.
Note that <hunkaddress> is not optional when you
are loading the source for a dummy debug task
source w <address> Use this command to see in which source file and
on which line a specific address is located
source t <tab size> Set the tab size used for the source display. The
default tab value is 8
source s Show all sources for the current debug task
source r Redisplay the source in the 'Source' logical window
source c Clear all sources and unload them
source g <line> Move the source to a specific line
trace Trace one instruction (singlestep mode)
trace n <number> Trace <number> instructions (singlestep mode)
trace b Trace until the next change of program flow
(singlestep mode)
trace t Trace over JSR or BSR. IF the instruction is
not a BSR or JSR this command is analogous to
'trace' (execute mode)
trace j Trace until a library ROM function is about
to be called with JMP ...(a6) or JSR ...(a6).
(singlestep mode)
trace r <register> Trace until a specified register is changed.
Register can be d0-d7, a0-a6 or sp.
(singlestep mode)
trace u <address> Trace until programcounter is equal to <address>.
This command works by setting a private
breakpoint (number 0) at <address>. This command
only works when <address> is not in ROM
(execute mode)
trace ut <address> Trace until programcounter is equal to <address>.
No breakpoint is set by this command. <address>
can be in ROM
(singlestep mode)
trace o Trace over the current instruction. This command
is analogous to 'trace u' with <address> equal
to the instruction following the current instruction
(execute mode)
trace ot Trace over the current instruction.
This version can be used in ROM
(singlestep mode)
trace c <condition> Trace until <condition> is true
(singlestep mode)
trace s Skip instruction
trace i Do not trace. Show the current registers and
instructions (obsolete in the fullscreen debugger)
trace g Trace until a breakpoint is encountered (note that
all previous trace commands also stop when a
breakpoint is encountered)
(execute mode)
trace gt Trace until a breakpoint is encountered
(singlestep mode)
trace h Interrupt the tracing or executing of the
current debug task
trace f Interrupt the tracing or executing of the
current debug task as soon as this task
is in ready state
with <debug node> <command>
Temporarily set the current debug node and execute
<command>. This is useful for example, if you are
debugging with multiple programs at the same time and
you want to have a look at the symbols or registers
of the other program